package com.ruoyi.iot.exchange.network.tcp.handler.dispatch;


import com.alibaba.fastjson.JSON;
import com.ruoyi.iot.domain.IotDevice;
import com.ruoyi.iot.domain.IotFrame;
import com.ruoyi.iot.domain.dto.ExchangeDTO;
import com.ruoyi.iot.exchange.facade.ServerFacade;
import com.ruoyi.iot.exchange.network.tcp.cache.TcpChannelCache;
import com.ruoyi.iot.exchange.network.tcp.notify.DeviceStateNotify;
import com.ruoyi.iot.rabbitmq.ExchangeRabbitHelper;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.xmlbeans.impl.util.HexBin;

@Slf4j
public abstract class TCPDispatchHandler implements ITcpDispatchHandler {

    public TCPDispatchHandler head = null;
    public TCPDispatchHandler next = null;

    protected IotFrame iotFrame;
    public TCPDispatchHandler(IotFrame iotFrame) {
        this.iotFrame = iotFrame;
    }


    protected ByteBuf in;
    protected ChannelHandlerContext channel;
    public void injectByteBuf(ChannelHandlerContext channel,ByteBuf in) {
        this.in = in;
        this.channel = channel;
    }

    public final void handlerFrame() {

        if(isThisFrame()) {
            in.resetReaderIndex();
            log.info(String.format("dev:%s handler:%s", TcpChannelCache.getDevId(channel),getClass().getSimpleName()));
            decodeFrame();
            in.markReaderIndex();
        } else if(next != null) {
            log.info(String.format("next:%s handle",next.getClass().getSimpleName()));
            next.handlerFrame();
        } else {
            if(in.isReadable()) {
                in.readByte();
                in.markReaderIndex();
                head.handlerFrame();
            } else {
                log.info("not find handler");
            }
        }

    }

    @Override
    public boolean isThisFrame() {

        byte[] preBuff =  takePreDataBuff();
        //log.info("preBuff:" + HexBin.bytesToString(preBuff));
        if(preBuff == null) {
            return false;
        }
        String markIndex = iotFrame.getMarkIndexList();
        String[] indexTexts = markIndex.split(",");
        byte[] headBuff = new byte[indexTexts.length];
        for(int i =0; i < indexTexts.length; i++) {
            Integer index = Integer.parseInt(indexTexts[i]);
            headBuff[i] = preBuff[index];
        }
        String headHex =  HexBin.bytesToString(headBuff);
        String markHex = iotFrame.getMarkFlag();
        //log.info("iotFrame:" + JSON.toJSONString(iotFrame));
        log.info("headHex:" + headHex + "  markHex:" + markHex + " equal:" + headHex.equals(markHex));
        if(!headHex.equals(markHex)) {
            return false;
        }
        return true;
    }

    private byte[] takePreDataBuff() {
        Integer preReadLen = iotFrame.getPreReadLen().intValue();
        if(in.readerIndex() + preReadLen  > in.writerIndex() ) {
            return null;
        }
        byte[] preBuff = new byte[preReadLen];
        in.readBytes(preBuff);
        in.resetReaderIndex();
        return preBuff;
    }


    @Override
    public void decodeFrame() {
        byte[] frame = trimFrame();

        if(frame == null) {
            log.info("frame is empty!");
            return;
        }
        //起到注册帧的作用，写入后就不处理了
        String devCode = TcpChannelCache.getDevId(channel);
        if(devCode == null) {
             String devId = takeDevId(frame);
             if(devId == null) {
                 log.info("not find device info,please check mysql config!");
                 return;
             }
             log.info("dev:" + devId + " online");
             DeviceStateNotify.getInstance().online(devId);
             TcpChannelCache.putNettyChannel(devId,channel);
             return;
        }

        IotDevice iotDeviceDTO = findDevice(devCode);
        log.info("devCode:" + devCode);
        if(iotDeviceDTO == null) {
            log.info(String.format("devCode:%s loss!",devCode));
            return;
        }
        ExchangeDTO exchangeDTO = new ExchangeDTO();
        byte[] hexFrame = patternHex(frame);
        exchangeDTO.setData(hexFrame);
        exchangeDTO.setDevId(devCode);

        exchangeDTO.setProductId(iotDeviceDTO.getProductId());
        ExchangeRabbitHelper.getInstance().devPublishToMQ(exchangeDTO);
        log.info("exchangeDTO:" + exchangeDTO.toString());
    }

    /**
     * 将frame转换
     * @param frame
     * @return
     */
    protected abstract byte[] patternHex(byte[] frame);

    /**
     * 绑定注册镇与通道
     * @param frame
     */
    protected abstract String takeDevId(byte[] frame);






    /**
     * 找到对应的设备
     * @param devCode 设备id
     * @return
     */
    protected IotDevice findDevice(String devCode) {

        IotDevice iotDevice = ServerFacade.getInstance().iotDeviceService.selectIotDeviceByDevNum(devCode);

        return iotDevice;
    }


    /**
     * 截获Hex数据
     * @return
     */
    protected final byte[] trimFrame() {
        if(iotFrame.getLen() == null)
            return null;
        Integer frameLen = iotFrame.getLen().intValue();
        log.info("len:" + frameLen);
        byte[] frame = null;
        if (frameLen > 0) {
            frame = new byte[frameLen];
        } else {

            byte[] preBuff = takePreDataBuff();
            String[] preLenTexts = iotFrame.getLenIndexList().split(",");
            log.info("index size:" + preLenTexts.length);
            byte[] preDataLenBuff = new byte[preLenTexts.length];
            for (int i = 0; i < preLenTexts.length; i++) {
                Integer dataLenPreIndex = Integer.parseInt(preLenTexts[i]);
                preDataLenBuff[i] = preBuff[dataLenPreIndex];
            }
            //注册帧默认为 Int类型
            log.info("dataLen buff:" + HexBin.bytesToString(preDataLenBuff));
            Integer dataLen = (getDataLen(preDataLenBuff) + 256) % 256;
            log.info("dataLen: " + dataLen);
            Integer totalLen = dataLen + iotFrame.getOtherLen().intValue() ;
            log.info("totalLen: " + totalLen);
            frame = new byte[totalLen];
        }

        //读取指令的帧数据长度
        Integer count = 0;
        do {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
            count ++;
            if(count >= 3) {
              log.info("frame pre read error");
              return null;
            }
        } while (in.readableBytes() < frame.length);

        in.readBytes(frame);
        log.info("base desc:" + JSON.toJSONString(iotFrame));
        int dataLen = frame.length - (iotFrame.getDataLeftVector().intValue() + iotFrame.getDataRightVector().intValue());
        log.info("data len:" + dataLen);
        byte[] dataBuff = new byte[dataLen];
        int j = 0;
        for(int i = iotFrame.getDataLeftVector().intValue(); i < frame.length - iotFrame.getDataRightVector(); i++) {
            dataBuff[j] = frame[i];
             j++;
        }
        log.info("data frame: " + HexBin.bytesToString(dataBuff));

        return dataBuff;
    }

    /**
     * 获取预读的数据长度
     * @param dataLenBuff 预读数据长度
     * @return
     */
    protected abstract Integer getDataLen(byte[] dataLenBuff);


}
